﻿using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections.Generic;

namespace PWEmu.Helpers
{
    /// <summary>
    /// Creates instance for INI file loading.
    /// TODO: mono variation.
    /// </summary>
    public class Config
    {
        #region Platform invoke

        [DllImport("kernel32", EntryPoint = "GetPrivateProfileStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern int GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, string lpReturnString, int nSize, string lpFilename);

        [DllImport("kernel32", EntryPoint = "WritePrivateProfileStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern int WritePrivateProfileString(string lpAppName, string lpKeyName, string lpString, string lpFilename);

        #endregion

        #region Static reading and writing

        /// <summary>
        /// Gets the value for some key in some section in the defined ini file.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <param name="section">The section</param>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value</param>
        /// <returns></returns>
        public static string Read(string iniFile, string section, string key, string defaultValue = "")
        {
            string returnString = new string(' ', 1024);
            GetPrivateProfileString(section, key, defaultValue, returnString, 1024, iniFile);
            return returnString.TrimEnd('\0');
        }

        /// <summary>
        /// Gets the boolean value for some key in some section in the defined ini file.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <param name="section">The section</param>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value</param>
        /// <returns></returns>
        public static bool ReadBoolean(string iniFile, string section, string key, bool defaultValue = false)
        {
            string returnString = new string(' ', 1024);
            GetPrivateProfileString(section, key, defaultValue ? "true" : "false", returnString, 1024, iniFile);
            return returnString.TrimEnd('\0') == "true";
        }

        /// <summary>
        /// Gets the integer value for some key in some section in the defined ini file.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <param name="section">The section</param>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value</param>
        /// <returns></returns>
        public static int ReadInt32(string iniFile, string section, string key, int defaultValue = 0)
        {
            string returnString = new string(' ', 1024);
            GetPrivateProfileString(section, key, defaultValue.ToString(), returnString, 1024, iniFile);
            return Int32.Parse(returnString.TrimEnd('\0'));
        }


        /// <summary>
        /// Gets the float value for some key in some section in the defined ini file.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <param name="section">The section</param>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value</param>
        /// <returns></returns>
        public static float ReadSingle(string iniFile, string section, string key, float defaultValue = 0.0f)
        {
            string returnString = new string(' ', 1024);
            GetPrivateProfileString(section, key, defaultValue.ToString(), returnString, 1024, iniFile);
            return Single.Parse(returnString.TrimEnd('\0'));
        }

        /// <summary>
        /// Gets the keys in some section.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <param name="section">The section</param>
        public static List<string> ReadKeys(string iniFile, string section)
        {
            string returnString = new string(' ', 32768);
            GetPrivateProfileString(section, null, null, returnString, 32768, iniFile);
            List<string> result = new List<string>(returnString.Split('\0'));
            result.RemoveRange(result.Count - 2, 2);
            return result;
        }

        /// <summary>
        /// Gets all sections.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <returns></returns>
        public static List<string> ReadSections(string iniFile)
        {
            string returnString = new string(' ', 65536);
            GetPrivateProfileString(null, null, null, returnString, 65536, iniFile);
            List<string> result = new List<string>(returnString.Split('\0'));
            result.RemoveRange(result.Count - 2, 2);
            return result;
        }

        /// <summary>
        /// Sets the value for some key in some section in the defined ini file.
        /// </summary>
        /// <param name="section"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void Write(string iniFile, string section, string key, string value)
        {
            WritePrivateProfileString(section, key, value, iniFile);
        }


        /// <summary>
        /// Sets the value for some key in some section in the ini file.
        /// </summary>
        /// <param name="section"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void Write(string iniFile, string section, string key, bool value)
        {
            WritePrivateProfileString(section, key, value ? "true" : "false", iniFile);
        }

        /// <summary>
        /// Sets the value for some key in some section in the ini file.
        /// </summary>
        /// <param name="section"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void Write(string iniFile, string section, string key, int value)
        {
            WritePrivateProfileString(section, key, value.ToString(), iniFile);
        }

        /// <summary>
        /// Sets the value for some key in some section in the ini file.
        /// </summary>
        /// <param name="section"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void Write(string iniFile, string section, string key, float value)
        {
            WritePrivateProfileString(section, key, value.ToString(), iniFile);
        }

        #endregion

        #region Fields and Properties

        /// <summary>
        /// Ini file we're working on.
        /// </summary>
        private string _iniFile;

        /// <summary>
        /// Gets the ini file path.
        /// </summary>
        public string IniFile { get { return _iniFile; } }

        /// <summary>
        /// The codepage of ini file we're working on.
        /// </summary>
        private int _codepage;

        /// <summary>
        /// Gets or sets the codepage.
        /// </summary>
        public int Codepage { get { return _codepage; } set { _codepage = value; } } 

        #endregion

        #region Internal reading and writing

        /// <summary>
        /// Config constructor.
        /// </summary>
        /// <param name="iniFile">File to be opened as config</param>
        public Config(string iniFile)
        {
            _iniFile = iniFile;
            _codepage = 1251;
        }

        /// <summary>
        /// Gets the value for some key in some section in the ini file.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <param name="section">The section</param>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value</param>
        /// <returns></returns>
        public string Read(string section, string key, string defaultValue = "")
        {
            string returnString = new string(' ', 1024);
            GetPrivateProfileString(section, key, defaultValue, returnString, 1024, _iniFile);
            return returnString.TrimEnd('\0');
        }

        /// <summary>
        /// Gets the value for some key in some section in the ini file.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <param name="section">The section</param>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value</param>
        /// <returns></returns>
        public string ReadString(string section, string key, string defaultValue = "")
        {
            string returnString = new string(' ', 1024);
            GetPrivateProfileString(section, key, defaultValue, returnString, 1024, _iniFile);
            return Encoding.UTF8.GetString(Encoding.GetEncoding(_codepage).GetBytes(returnString.Trim())).Trim('\0');
        }

        /// <summary>
        /// Gets the boolean value for some key in some section in the ini file.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <param name="section">The section</param>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value</param>
        /// <returns></returns>
        public bool ReadBoolean(string section, string key, bool defaultValue = false)
        {
            string returnString = new string(' ', 1024);
            GetPrivateProfileString(section, key, defaultValue ? "true" : "false", returnString, 1024, _iniFile);
            return returnString.Trim().CompareTo("true") == 0;
        }

        /// <summary>
        /// Gets the integer value for some key in some section in the ini file.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <param name="section">The section</param>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value</param>
        /// <returns></returns>
        public int ReadInt32(string section, string key, int defaultValue = 0)
        {
            string returnString = new string(' ', 1024);
            GetPrivateProfileString(section, key, defaultValue.ToString(), returnString, 1024, _iniFile);
            return Int32.Parse(returnString.Trim());
        }


        /// <summary>
        /// Gets the float value for some key in some section in the ini file.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <param name="section">The section</param>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value</param>
        /// <returns></returns>
        public float ReadSingle(string section, string key, float defaultValue = 0.0f)
        {
            string returnString = new string(' ', 1024);
            GetPrivateProfileString(section, key, defaultValue.ToString(), returnString, 1024, _iniFile);
            return Single.Parse(returnString.Trim());
        }

        /// <summary>
        /// Gets the keys in some section.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <param name="section">The section</param>
        public List<string> ReadKeys(string section)
        {
            string returnString = new string(' ', 32768);
            GetPrivateProfileString(section, null, null, returnString, 32768, _iniFile);
            List<string> result = new List<string>(returnString.Split('\0'));
            result.RemoveRange(result.Count - 2, 2);
            return result;
        }

        /// <summary>
        /// Gets all sections.
        /// </summary>
        /// <param name="iniFile">The ini file</param>
        /// <returns></returns>
        public List<string> ReadSections()
        {
            string returnString = new string(' ', 65536);
            GetPrivateProfileString(null, null, null, returnString, 65536, _iniFile);
            List<string> result = new List<string>(returnString.Split('\0'));
            result.RemoveRange(result.Count - 2, 2);
            return result;
        }

        /// <summary>
        /// Sets the value for some key in some section in the ini file.
        /// </summary>
        /// <param name="section"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Write(string section, string key, string value)
        {
            WritePrivateProfileString(section, key, value, _iniFile);
        }

        /// <summary>
        /// Sets the value for some key in some section in the ini file.
        /// </summary>
        /// <param name="section"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Write(string section, string key, bool value)
        {
            WritePrivateProfileString(section, key, value ? "true" : "false", _iniFile);
        }

        /// <summary>
        /// Sets the value for some key in some section in the ini file.
        /// </summary>
        /// <param name="section"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Write(string section, string key, int value)
        {
            WritePrivateProfileString(section, key, value.ToString(), _iniFile);
        }

        /// <summary>
        /// Sets the value for some key in some section in the ini file.
        /// </summary>
        /// <param name="section"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Write(string section, string key, float value)
        {
            WritePrivateProfileString(section, key, value.ToString(), _iniFile);
        }

        #endregion

    }
}

